local ac = ac
local jass = require 'jass.common'
local japi = require 'jass.japi'
local code = require 'jass.code'
local hook = require 'jass.hook'
local dbg = require 'jass.debug'

local GetPlayerName = code['GetPlayerName2'] or jass['GetPlayerName']

---@class player "玩家"
local player = {}
setmetatable(player,player)
ac.player = player 

-- 测试状态
-- player.complete_test = true 

-- 在线玩家数量
player.online_count = 0

-- 在线玩家组
player.online_players = {}

function player.get_online_count()
	return player.online_count
end 

function player.get_online_players()
	return player.online_players
end 

function player:__call(handle)
	return player[handle]
end

function player:__tostring()
	return ('玩家[%d]-%s'):format(self:get_id(),self:get_name())
end

local mt = {}
player.__index = mt 

--- 玩家
mt.type = 'player'

--- 玩家颜色
mt.color = nil

--- 玩家队伍
mt.team = nil

--- 句柄
mt.handle = jass.ConvertUnitState(0) 

--- 派发全局事件(有返回值)
--- @param name string
--- @param ... any
--- @return function
function mt:dispatch(name, ...)
	local ok, res = ac.dispatch(self, name, ...)
	if ok ~= nil then 
		return ok, res 
	end 
    return ac.dispatch(ac.game,name,...)
end

--- 派发全局事件
--- @param name string
--- @param ... any
--- @return function
function mt:notify(name, ...)
	ac.notify(self, name, ...)
    ac.notify(ac.game,name,...)
end

--- 注册全局事件
--- @param name string
--- @return function
function mt:event(name)
	return ac.register(self, name)
end

mt.on = mt.event 
mt.register = mt.event


--- 获取ID
function mt:get_id()
    return (jass.GetPlayerId(self.handle) + 1)
end 

-- 获取初始名字
function mt:get_default_name()
	return self.default_name
end 

--- 获取名字
function mt:get_name()
	return self.name 
end  

--- 设置玩家名字
function mt:set_name(name)
	self.name = name
	jass.SetPlayerName(self.handle, name)
end

-- 获取当前名字
function mt:get_current_name()
    return GetPlayerName(self.handle)
end 

player.colors = {
	"|cFFFF0303",
    "|cFF0042FF",
    "|cFF1CE6B9",
    "|cFF540081",
    "|cFFFFFC01",
    "|cFFFE8A0E",
    "|cFF20C000",
    "|cFFE55BB0",
    "|cFF959697",
    "|cFF7EBFF1",
    "|cFF106246",
    "|cFF4E2A04",
    "|cFF282828",
    "|cFF282828",
    "|cFF282828",
    "|cFF282828",
}

--- 获取世界坐标的名字
function mt:get_world_name()
	local user = ac.player.self
    local name = self:get_default_name()
	if user == self then 
		return ("|cff00ff00%s|r"):format(name)
	elseif user:is_enemy(self) then 
		return ("|cffff0000%s|r"):format(name)
	end
	return ("|cffffcc00%s|r"):format(name)
end 

--- 获取名字
function mt:get_color_name()
    return player.colors[self:get_id()] .. self:get_current_name() .. "|r"
end 

---获取玩家颜色
function mt:get_color()
	local id = self:get_id()
    return player.colors[id]  
end

--- 是否是本地玩家
function mt:is_self()
	return self == player.self
end

--- 获取所有者
function mt:get_owner()
	return self
end

--- 中立敌对
function mt:is_creep(dest)
	return jass.GetPlayerController(self.handle) == jass.MAP_CONTROL_CREEP
end 

--- 中立可营救
function mt:is_rescuable()
	return jass.GetPlayerController(self.handle) == jass.MAP_CONTROL_RESCUABLE
end 

--- 玩家是 中立被动
function mt:is_neutral()
	return jass.GetPlayerSlotState(self.handle) == jass.MAP_CONTROL_NEUTRAL
end 

--- 由电脑控制
function mt:is_computer()
	if player.complete_test and self:get_id() <= 12 and self:get_id() ~= 9 then 
		return false  
	end 
	return jass.GetPlayerController(self.handle) == jass.MAP_CONTROL_COMPUTER
end 

--- 是玩家控制
function mt:is_player()
	if player.complete_test and self:get_id() <= 12 and self:get_id() ~= 9 then 
		return true 
	end 
	return jass.GetPlayerController(self.handle) == jass.MAP_CONTROL_USER
end

--- 正在游戏中
function mt:is_playing()
	if player.complete_test and self:get_id() <= 12 and self:get_id() ~= 9 then 
		return true 
	end 
	return jass.GetPlayerSlotState(self.handle) == jass.PLAYER_SLOT_STATE_PLAYING
end 

--- 是在线玩家
function mt:is_online()
	return self:is_player() and self:is_playing()
end

--- 设置玩家结盟 
--- @params dest 目标玩家 
--- @params flag 是否双向 默认为false
function mt:ally(dest,flag)
	jass.SetPlayerAlliance(self.handle,dest.handle, jass.ALLIANCE_PASSIVE, true)
	if flag then
		dest:ally(self)
	end
end

--- 设置玩家敌对
--- @params dest 目标玩家 
--- @params flag 是否双向 默认为false
function mt:enemy(dest,flag)
	jass.SetPlayerAlliance(self.handle, dest.handle, jass.ALLIANCE_PASSIVE, false)
	if flag then
		dest:enemy(self)
	end
end

--- 获取玩家队伍ID
function mt:get_team_id()
	return jass.GetPlayerTeam(self.handle)
end 

--- 设置玩家队伍ID
function mt:set_team_id(id)
	jass.SetPlayerTeam(self.handle, id)
end 

--- 是否为敌人
function mt:is_enemy(dest)
	return jass.IsPlayerEnemy( dest.handle , self.handle )
end

--- 是否为友军
function mt:is_ally(dest)
	return jass.IsPlayerAlly( dest.handle , self.handle )
end

-- 玩家是 该玩家的联盟目标
function mt:is_league(dest)
	if self == dest then 
		return true 
	end 
	if dest:is_neutral() or dest:is_creep() then
		return false 
	end 
	if dest:get_team_id() < 0 then 
		return dest:is_ally(self)
	end 
	return (dest:get_team_id() == self:get_team_id()) and dest:is_ally(self)
end 


local shared_states = {
	['视野'] = jass.ConvertAllianceType(5),		-- 视野
	['单位'] = jass.ConvertAllianceType(6),		-- 单位
	['完全控制权'] = jass.ConvertAllianceType(7),
}

--- 设置玩家联盟状态
--- @params dest 目标玩家 
--- @params name 联盟状态
--- @params flag 是否开启
function mt:shared(dest, name, flag)
	local state = shared_states[name]
	if state then 
		jass.SetPlayerAlliance(self.handle, dest.handle, state, flag and true or false)
	end
end

--- 判断玩家联盟状态
--- @returns boolean flag
function mt:is_shared(dest, name)
	local state = shared_states[name]
	if state then 
		return jass.GetPlayerAlliance(self.handle, dest.handle, state)
	end
	return false
end 

--- 获取在玩家A视角的玩家B联盟状态颜色
function mt:get_shared_flag(dest)
	if dest:is_shared(self, "单位") then 
		return 1, 0xff00ff00
	elseif dest:is_ally(self) then 
		return 2, 0xffffcc00
	elseif dest:is_enemy(self) then 
		return 3, 0xffff0000
	end 
	return 0, 0xffffffff
end 

--模拟退出 用模拟按键 按下Alt + q 、 q
function mt:exit()
    if self:is_self() then 
        japi.SendMessage(0x0010,0, 0)
        japi.SendMessage(0x00100,88, 0)
        japi.SendMessage(0x00101,88, 1)
    end 
end 

--设置玩家完全结盟 @目标玩家 @是否双向结盟 默认为false
function mt:all_flags_ally(dest, flag)
	jass.SetPlayerAlliance(self.handle, dest.handle, jass.ALLIANCE_PASSIVE, true)
	jass.SetPlayerAlliance(self.handle, dest.handle, jass.ALLIANCE_HELP_REQUEST, true)
	jass.SetPlayerAlliance(self.handle, dest.handle, jass.ALLIANCE_HELP_RESPONSE, true)
	jass.SetPlayerAlliance(self.handle, dest.handle, jass.ALLIANCE_SHARED_XP, true)
	jass.SetPlayerAlliance(self.handle, dest.handle, jass.ALLIANCE_SHARED_SPELLS, true)
	if flag then
		dest:all_flags_ally(self)
	end
end

--设置玩家完全敌对 @目标玩家 @是否双向结盟 默认为false
function mt:all_flags_enemy(dest, flag)
	jass.SetPlayerAlliance(self.handle, dest.handle, jass.ALLIANCE_PASSIVE, false)
	jass.SetPlayerAlliance(self.handle, dest.handle, jass.ALLIANCE_HELP_REQUEST, false)
	jass.SetPlayerAlliance(self.handle, dest.handle, jass.ALLIANCE_HELP_RESPONSE, false)
	jass.SetPlayerAlliance(self.handle, dest.handle, jass.ALLIANCE_SHARED_XP, false)
	jass.SetPlayerAlliance(self.handle, dest.handle, jass.ALLIANCE_SHARED_SPELLS, false)
	if flag then
		dest:all_flags_enemy(self)
	end
end

--- 发送消息 (可持续时间,默认10秒)
function mt:message(text,time)
    if time then 
        jass.DisplayTimedTextToPlayer(self.handle,0,0,time,text)
    else 
        jass.DisplayTextToPlayer(self.handle,0,0,text)
    end
end

function mt:send_msg(text,time)
    self:message(text,time)
end


--- 清空屏幕显示
function mt:clear_message()
	if self == player.self then
		jass.ClearTextMessages()
	end
end

--- 小地图ping点
function mt:ping(dest,time)
	local x,y = dest:get_point():get()
	if self:is_self() then 
		jass.PingMinimap(x,y, time or 1)
	end 
end

--停止震动
function mt:stop_noise()
	self._noise = false
	if self:is_self() then 
		jass.CameraSetSourceNoise(0, 0)
		jass.CameraSetTargetNoise(0, 0)
	end
end

--震动屏幕 @振动幅度 @震动时间
function mt:noise(magnitude,time)
	if self._noise then
		self:stop_noise()
	end
	self._noise = true
	local richter = math.min( math.max( magnitude , 2.0) , 5.0)
	if self:is_self() then 
		jass.CameraSetTargetNoiseEx(magnitude*2.0, magnitude* 10^richter,true)
		jass.CameraSetSourceNoiseEx(magnitude*2.0, magnitude* 10^richter,true)
	end
	time = time or 0.5
	ac.wait( time * 1000,function()
		self:stop_noise()
	end)
end


--- 命令玩家选中单位
function mt:select(u)
	if self:is_self() then
		jass.ClearSelection()
		jass.SelectUnit(u.handle, true)
		-- jass.ForceUICancel()
	end
end

function mt:select_unit(u)
    self:select(u)
end

--- 命令玩家取消选择某单位
function mt:un_select(u)
	if self:is_self() then
		if u ~= nil then 
			jass.SelectUnit(u.handle, false)
		else 
			jass.ClearSelection()
		end
	end
end

--- 命令玩家添加选择某单位
function mt:add_select(u)
	if self:is_self() then
		jass.SelectUnit(u.handle, true)
	end
end

--当前选择单位
function mt:get_select_unit()
    return self.selecting
end

--- 获取商店单位选择目标
function mt:get_store_target(unit)
    return ac.unit(japi.GetStoreTarget(unit.handle, self.handle))
end

local camera_state = {
	['镜头距离'] 		= jass.CAMERA_FIELD_TARGET_DISTANCE,
	['远景截断距离']	= jass.CAMERA_FIELD_FARZ,
	['镜头区域']		= jass.CAMERA_FIELD_FIELD_OF_VIEW,
	['x轴旋转角度']		= jass.CAMERA_FIELD_ANGLE_OF_ATTACK,
	['y轴旋转角度']		= jass.CAMERA_FIELD_ROLL,
	['z轴旋转角度']		= jass.CAMERA_FIELD_ROTATION,
	['z轴偏移']         = jass.CAMERA_FIELD_ZOFFSET
}

-- 获取镜头属性
function mt:get_camera_field(key)
	return jass.GetCameraField(camera_state[key]) -- math.deg()
end

-- 设置镜头属性,镜头属性,数值,[持续时间]
function mt:set_camera_field(key, value, time)
	if self == player.self then
		jass.SetCameraField(camera_state[key], value, time or 0)
	end
end

-- 设置玩家镜头位置(点,平滑时间)
function mt:set_camera(where,time)
	local x, y
	if where then
		x, y = where:get_point():get()
	else
		x, y = jass.GetCameraTargetPositionX(), jass.GetCameraTargetPositionY()
	end
	if player.self == self then
		if time then
			jass.PanCameraToTimed(x, y, time)
		else
			jass.SetCameraPosition(x, y)
		end
	end
end

--获取玩家镜头位置 ( 注！返回的是异步数据 )
function mt:get_camera_point()
    return ac.point(jass.GetCameraTargetPositionX(), jass.GetCameraTargetPositionY())
end

-- 滤镜
function mt:cinematic_filter(data)
	jass.SetCineFilterTexture(data.file or [[ReplaceableTextures\CameraMasks\DreamFilter_Mask.blp]])
	jass.SetCineFilterBlendMode(jass.BLEND_MODE_BLEND)
	jass.SetCineFilterTexMapFlags(jass.TEXMAP_FLAG_NONE)
	jass.SetCineFilterStartUV(0, 0, 1, 1)
	jass.SetCineFilterEndUV(0, 0, 1, 1)
	if data.start then
		jass.SetCineFilterStartColor(data.start[1] * 2.55, data.start[2] * 2.55, data.start[3] * 2.55, data.start[4] * 2.55)
	end
	if data.finish then
		jass.SetCineFilterEndColor(data.finish[1] * 2.55, data.finish[2] * 2.55, data.finish[3] * 2.55, data.finish[4] * 2.55)
	end
	jass.SetCineFilterDuration(data.time)
	if self == ac.player.self then
		jass.DisplayCineFilter(true)
	end

	local player = self
	function data:remove()
		if player:is_self() then
			jass.DisplayCineFilter(false)
		end
	end

	return data
end


-- 获取玩家属性
function mt:get(name)
	return self.resource[name]
end 

-- 设置玩家属性
function mt:set(name,value)
	if type(value) ~= 'number' then 
		value = tonumber(value) or 0
	end 
	self.resource[name] = math.floor(value)
end 

-- 增加玩家属性
function mt:add(name,value)
	local current = self:get(name) + value
	self:set(name, current)
end 

-- 设置自定义值
function mt:set_data(key,value)
    self.user_data = self.user_data or {}
    self.user_data[key] = value 
end

-- 获取自定义值
function mt:get_data(key,default)
    return self.user_data and self.user_data[key] or default
end

-- 判断是否解锁条件
function mt:has_lock(name)
    if self.locks[name] then 
        return self.locks[name]
    end 
    return false
end

-- 解锁条件
function mt:unlock(name)
	self.locks[name] = true 
end 

-- 锁定条件
function mt:lock(name)
	self.locks[name] = false
end 

function ac.each_player(func)
    for i = 1, 12 do
        local player = ac.player(i)
        if player:is_online() then
            func(player)
        end
    end
end

ac.event.register("任意玩家-输入聊天信息", function()
    local trigplayer = ac.player(jass.GetTriggerPlayer())
	local trigstring = jass.GetEventPlayerChatString()
	--trigplayer:notify('玩家-输入聊天信息', trigplayer, trigstring)
	trigplayer:notify('玩家-聊天', trigplayer, trigstring)
end)

ac.event.register("任意玩家-离开游戏", function()
    local trigplayer = ac.player(jass.GetTriggerPlayer())
	player.online_count = player.online_count - 1
	player.online_players[trigplayer] = nil
	trigplayer:notify('玩家-离开游戏', trigplayer)
end)

ac.event.register("任意玩家-按下ESC", function()
    local trigplayer = ac.player(jass.GetTriggerPlayer())
	trigplayer:notify('玩家-按下ESC', trigplayer)
end)

ac.event.register("任意单位-被选择", function()
    local trigplayer = ac.player(jass.GetTriggerPlayer())
    local trigunit = ac.unit(jass.GetTriggerUnit())
	if trigplayer and trigunit then 
		trigplayer.selecting = trigunit 
		trigplayer:notify('玩家-选择单位', trigplayer, trigunit)
		trigunit:notify('单位-被玩家选择', trigunit, trigplayer)
	end
end)

ac.event.register("任意单位-被取消选择", function()
    local trigplayer = ac.player(jass.GetTriggerPlayer())
    local trigunit = ac.unit(jass.GetTriggerUnit())
	if trigplayer and trigunit and trigplayer.selecting == trigunit then 
		trigplayer:notify('玩家-取消选择单位', trigplayer, trigunit)
		trigunit:notify('单位-被玩家取消选择', trigunit, trigplayer)
		trigplayer.selecting = nil 
	end
end)

--- 初始化
function player.init()

    for id = 1,16 do 
        local handle = jass.Player(id-1)
		--- 句柄引用
		dbg.handle_ref(handle)

        local object = setmetatable({
			handle = handle,
            id = id,
			name = GetPlayerName(handle),
			default_name = GetPlayerName(handle),
			upgrades = {},
			resource = {},
			archives = {},
			skins	= {},	-- 存放模型皮肤路径
			locks	= {},
		},player)

		object.gchash = handle 
		dbg.gchash(object, handle)

        player[id] = object
        player[handle] = object
		if object:is_online() then 
			player.online_players[object] = true
			player.online_count = player.online_count + 1
		end
        
    end 
    player.self = player(jass.GetLocalPlayer())
    log.debug("本地玩家", player.self)
end

return player